package modelovectorial.core;

import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.SimpleTextExtractionStrategy;
import com.itextpdf.text.pdf.parser.TextExtractionStrategy;
import java.io.File;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import modelovectorial.DocumentoPDF;
import modelovectorial.Palabra;
import modelovectorial.persistencia.GestorInsercion;
import soporte.StopWords;
import soporte.WordCleaner;
import utn.frc.tsb.utils.ValerioFritelli.HashTable.HashList;

/**
 * @project DLC Final / UTN FRC 2013
 * @date Febrero de 2013
 *
 * @author Gonzalo Arribere
 * @author Gustavo Morales
 * @author Pablo Moreno
 *
 * Esta clase se encarga de realizar el procesamiento de cada documento PDF
 * desglosando el mismo en cada una de sus palabras.
 */
public class DocumentParser {

    /**
     * Tamaño inicial de la TablaHash que recibirá las palabras unicas del
     * documento actual parseado
     */
    public static final int INITIAL_WORDS_LENGTH = 30000;
    private HashList mPalabras; //Tabla Hash que almacenara las palabras que se lean del documento en proceso.
    private File mPdfFile;//Objeto File que representara el archivo pdf del documento.
    private DocumentoPDF mDocumento;//Objeto documento que representara al pdf en curso dentro de la aplicacion.

    /**
     * Analiza el nombre del archivo para poder dividirlo en su titulo y autor
     * para luego ser almacenado en la base.
     *
     * @param pFileName nombre del archivo.
     * @return un arreglo con dos espacios de cadenas que contendra el titulo y
     * el autor del pdf.
     */
    private String[] analizeFileName(String pFileName) {
        String pResult[] = new String[2];

        pFileName = pFileName.substring(0, pFileName.length() - 4);
        StringTokenizer work = new StringTokenizer(pFileName, "-");

        if (work.hasMoreElements()) {
            pResult[0] = work.nextToken().trim();
        }
        if (work.hasMoreElements()) {
            pResult[1] = work.nextToken().trim();
        }

        return pResult;
    }

    /**
     * Realiza el procesamiento e invoca la persistencia del documento y sus
     * palabras en la base de datos.
     */
    private void procesarDocumento() {
        //Tengo que recorrer la tabla hash preguntando por el maximo "nR" de cada elemento.
        int max = 0;
        int frecuenciaTermino;

        Palabra p;
        mPalabras.startIterator();

        //cada vuelta se analiza una palabra.
        GestorInsercion insercion = GestorInsercion.getInstance();

        while (mPalabras.hasNext()) {
            p = (Palabra) mPalabras.next();

            frecuenciaTermino = p.getTfDoc();

            // la insercion ya verifica si existe la palabra y tiene en cuenta el nr,
            // tambien graba los terminos sucios de la palabra.
            insercion.grabarPalabra(mDocumento, p);


            // pregunto si es una stopword y ademas pregunto si tiene la mayor de las frecuencias.
            if (!StopWords.isStopWord(p.getPalabraLimpia()) && frecuenciaTermino > max) {
                max = frecuenciaTermino;
            }
        }

        mDocumento.setMaxTf(max);

        insercion.executePosteoBatch();
        insercion.executeIndicesBatch();
    }

    /**
     * Indexa un archivo PDF en la base de datos preparando los datos para la
     * aplicacion del model vectorial.
     *
     * @param pDirectory
     * @param pFileName
     */
    public void parseDocument(String pDirectory, String pFileName) {
        mPdfFile = new File(pDirectory + File.separator + pFileName);

        String[] info = analizeFileName(pFileName);

        String autor = info[0];
        String titulo = info[1];

        //creo un objeto documento con los datos del PDF
        mDocumento = new DocumentoPDF(titulo, autor, pFileName, 0, mPdfFile.length(), 0);
        mPalabras = new HashList(INITIAL_WORDS_LENGTH);

        mDocumento.persistir();

        boolean success = false;
        try {
            parsePDFFile();
            success = true;
        } catch (Exception e) {
            Logger.getLogger(DocumentParser.class.getName()).log(Level.SEVERE, null, e);
        }

        if (success) {
            // Graba las palabras limpias, sucias, posteo y primeros N indices
            procesarDocumento();
            // Actualizar info del documento
            mDocumento.updatePersistencia();
            // Todo OK, confirmar inserts en la DB commit;
            GestorInsercion.getInstance().confirmarCambios();
        } else {
            // Algun fallo, deshacer lo insertado... rollback;
            GestorInsercion.getInstance().deshacerCambios();
        }
    }

    /**
     * Parsea un archivo PDF - documento creado: mDocumento - tabla hash de
     * terminos del archivo PDF: mPalabras (cada palabra puede contener una
     * lista de terminos sucios) ---- cada termino sucio puede contener una
     * lista de indices (Integer's)
     *
     * @throws Exception si algo falla
     */
    private void parsePDFFile() throws Exception {

        PdfReader reader = new PdfReader(mPdfFile.getAbsolutePath().replace("\\", "\\\\"));//crea un objeto lect

        mDocumento.setPaginas(reader.getNumberOfPages());//guardo en una variable entera la cantidad de palabras que posee el documento.
        //crea un objecto lector del contenido de lo que tiene el readeror de pdf
        PdfReaderContentParser parser = new PdfReaderContentParser(reader);

        TextExtractionStrategy strategy;
        StringTokenizer strWork;
        String word, wordClean;

        // Indice absoluta de cada palabra en el documento
        int j = 0;

        // auxiliar realIndex >= 0 Si solo si (realIndex <= Palabra.MAX_INDEX || pageNumber < Palabra.MAX_PAGE
        int realIndex;

        Palabra nuevaPalabra;
        Palabra palabraExistente;

        for (int i = 1; i <= reader.getNumberOfPages(); i++) {

            strategy = parser.processContent(i, new SimpleTextExtractionStrategy());
            strWork = new StringTokenizer(strategy.getResultantText());

            System.out.println("procesando pagina : " + i);

            while (strWork.hasMoreTokens()) {
                // Curar mal parseo
                word = WordCleaner.sanatize(strWork.nextToken());
                // Limpiar palabra, sin acentos, sin letras no a-z0-9, todo en minusculas, sin espacios
                wordClean = WordCleaner.clean(word);
                // Solo palabras que cumplan con las restricciones!!
                if (wordClean.length() == 0 || word.length() > Palabra.MAX_WORD_LENGTH) {
                    continue;
                }
                // si es valida, incremento el indice absoluto
                j++;

                // tomar en cuenta los indices, si el Indice < Palabra.MAX_INDEX || Pages <= Palabra.MAX_PAGE
                // si no cumple realIndex = -1  no es guardado en la DB
                realIndex = (j > Palabra.MAX_INDEX || i > Palabra.MAX_PAGE) ? -1 : j;

                nuevaPalabra = new Palabra(wordClean, word, realIndex);

                palabraExistente = (Palabra) mPalabras.get(nuevaPalabra);

                if (palabraExistente != null) {
                    palabraExistente.incrementarFrecuencia();
                    palabraExistente.agregarTerminoSucio(word, realIndex);
                } else {
                    //agrego el termino a la tabla
                    mPalabras.put(nuevaPalabra);
                    nuevaPalabra.incrementarFrecuencia();
                }
            }
        }
    }
}